#ifndef SEGMENTINVENTORY_TEST
#define SEGMENTINVENTORY_TEST

#include "SegmentInventory_Test.h"

/**
Creates a SegmentInventory + Manager, creates a Segment, allocates Pages and checks if the pages are present.
**/
void Hausaufgabe3_Test::test1()
{
    cout << "Start Test-1" << endl;
    createFile(DB_FILE.c_str(), DB_FILE_SIZE);
    //SegmentInventory& segInv = DB::getDB().getSegmentInventory();
    SegmentManager& segMan = DB::getDB().getSegmentManager();
    Segment& seg = segMan.createSegment();
    //segInv.printInventory();

    for (unsigned i = 0; i < DB_FILE_SIZE - 1; i++)
    {
        cout << "Test1: " << i << endl;
        assert(seg.requestPages(1, 1).size() == 1);
        assert(seg.getPageCount() == i + 1);
        BufferFrame& frame = seg.getPage(i, false);
        assert(frame.getPageId() > 0);
        seg.unfixPage(frame, false);
        //segInv.printInventory();
    }
    cout << "End Test-1" << endl;
};

/**
Like test1 but uses several Segments and  aquires pages randomly
**/
void Hausaufgabe3_Test::test2()
{
    cout << "Start Test-2" << endl;
    createFile(DB_FILE.c_str(), DB_FILE_SIZE);
    SegmentManager& segMan = DB::getDB().getSegmentManager();
    Segment& seg1 = segMan.createSegment();
    Segment& seg2 = segMan.createSegment();
    Segment& seg3 = segMan.createSegment();

    //segInv.printInventory();

    for (unsigned i = 0; i < DB_FILE_SIZE - 1; i++)
    {
        cout << "Test2: " << i << endl;
        Segment* segP;
        switch (i % 3)
        {
        case 0:
            segP = &seg1;
            break;
        case 1:
            segP = &seg2;
            break;
        default:
            segP = &seg3;

        }
        Segment& seg = *segP;
        assert(seg.requestPages(1, 1).size() == 1);
        assert(seg.getPageCount() == i / 3 + 1);
        BufferFrame& frame = seg.getPage(i / 3, false);
        assert(frame.getPageId() > 0);
        seg.unfixPage(frame, false);
        //segInv.printInventory();
    }
    cout << "End Test-2" << endl;
};

/**
Tests if pages are freed after destruction of a segment
**/
void Hausaufgabe3_Test::test3()
{
    cout << "Start Test-3" << endl;
    createFile(DB_FILE.c_str(), DB_FILE_SIZE);
    SegmentInventory& segInv = DB::getDB().getSegmentInventory();
    SegmentManager& segMan = DB::getDB().getSegmentManager();
    Segment& seg = segMan.createSegment();
    //segInv.printInventory();

    for (unsigned i = 0; i < DB_FILE_SIZE - 1; i++)
    {
        cout << "Test3: " << i << endl;
        assert(segInv.getFreeCount() == DB_FILE_SIZE - 1 - i);
        assert(seg.requestPages(1, 1).size() == 1);
        assert(segInv.getFreeCount() == DB_FILE_SIZE - 2 - i);
        assert(seg.getPageCount() == i + 1);
        BufferFrame& frame = seg.getPage(i, false);
        assert(frame.getPageId() > 0);
        seg.unfixPage(frame, false);
        //segInv.printInventory();
    }

    segMan.removeSegment(seg);
    assert(segInv.getFreeCount() == DB_FILE_SIZE - 1);
    //segInv.printInventory();

    cout << "End Test-3" << endl;
};

/**
Like test3 but with several Segments
**/
void Hausaufgabe3_Test::test4()
{
    cout << "Start Test-4" << endl;
    createFile(DB_FILE.c_str(), DB_FILE_SIZE);
    SegmentInventory& segInv = DB::getDB().getSegmentInventory();
    SegmentManager& segMan = DB::getDB().getSegmentManager();
    Segment& seg1 = segMan.createSegment();
    Segment& seg2 = segMan.createSegment();
    Segment& seg3 = segMan.createSegment();

    for (unsigned i = 0; i < DB_FILE_SIZE - 1; i++)
    {
        cout << "Test4: " << i << endl;
        Segment* segP;
        switch (i % 3)
        {
        case 0:
            segP = &seg1;
            break;
        case 1:
            segP = &seg2;
            break;
        default:
            segP = &seg3;

        }
        Segment& seg = *segP;
        assert(seg.requestPages(1, 1).size() == 1);
        assert(seg.getPageCount() == i / 3 + 1);
        BufferFrame& frame = seg.getPage(i / 3, false);
        assert(frame.getPageId() > 0);
        seg.unfixPage(frame, false);
        //segInv.printInventory();
    }

    assert(segInv.getFreeCount() == 0);
    unsigned currentSize = segInv.getFreeCount();
    segMan.removeSegment(seg1);
    assert(segInv.getFreeCount() > currentSize);
    //segInv.printInventory();

    currentSize = segInv.getFreeCount();
    segMan.removeSegment(seg2);
    assert(segInv.getFreeCount() > currentSize);
    //segInv.printInventory();

    currentSize = segInv.getFreeCount();
    segMan.removeSegment(seg3);
    assert(segInv.getFreeCount() > currentSize);
    //segInv.printInventory();

    cout << "End Test-4" << endl;
};

/**
Tests if the Testfile is growing if needed
**/
void Hausaufgabe3_Test::test5()
{
    cout << "Start Test-5" << endl;
    createFile(DB_FILE.c_str(), DB_FILE_SIZE);
    SegmentInventory& segInv = DB::getDB().getSegmentInventory();
    SegmentManager& segMan = DB::getDB().getSegmentManager();
    Segment& seg = segMan.createSegment();
    //segInv.printInventory();

    unsigned fileSize = DB_FILE_SIZE;

    for (unsigned i = 0; i < 2 * DB_FILE_SIZE - 1; i++)
    {
        cout << "Test5: " << i << endl;
        assert(seg.requestPages(1, 1).size() == 1);
        assert(seg.getPageCount() == i + 1);
        BufferFrame& frame = seg.getPage(i, false);
        assert(frame.getPageId() > 0);
        seg.unfixPage(frame, false);
        //segInv.printInventory();
    }

    assert(getFileSize() >= 2 * fileSize);
    segMan.removeSegment(seg);
    assert(segInv.getFreeCount() > 0);
    //segInv.printInventory();

    cout << "End Test-5" << endl;
};

/**
Concurrent Test
*/



void Hausaufgabe3_Test::test6()
{
    cout << "Start Test-6" << endl;
    createFile(DB_FILE.c_str(), DB_FILE_SIZE);
    SegmentManager& segMan = DB::getDB().getSegmentManager();

    pthread_t thread[THREAD_COUNT];
    for (unsigned i = 0; i < THREAD_COUNT; i++)
    {
        pthread_create (&thread[i], NULL, concSegTest, &segMan);
    }

    for (unsigned i = 0; i < THREAD_COUNT; i++)
    {
        pthread_join(thread[i], NULL);
    }

};

void *Hausaufgabe3_Test::concSegTest(void* arg)
{
    SegmentManager& segMan = *((SegmentManager*)arg);
    Segment& seg = segMan.createSegment();

    for (unsigned i = 0; i < THREAD_COUNT * 30; i++)
    {
        bla(i, seg);
        SLEEP(100);

        BufferFrame& frame = seg.getPage(i, false);
        char* data = (char*)frame.getData();
        for (unsigned n = 0; n < pageSize; n++)
        {
            assert(data[n] == (char)i);
        }
        seg.unfixPage(frame, false);
    }

    assert(seg.getPageCount() == THREAD_COUNT * 30);

    for (unsigned i = 0; i < THREAD_COUNT * 30; i++)
    {
        BufferFrame& frame = seg.getPage(i, false);
        char* data = (char*)frame.getData();
        for (unsigned n = 0; n < pageSize; n++)
        {
            assert(data[n] == (char)i);
        }
        seg.unfixPage(frame, false);
    }

    return NULL;
};

void Hausaufgabe3_Test::bla(unsigned i, Segment& seg)
{
    unsigned old = seg.getPageCount();
    seg.requestPages(3, 3);
    assert(seg.getPageCount() == old + 3);
    BufferFrame& frame = seg.getPage(i, false);
    char* data = (char*)frame.getData();
    for (unsigned n = 0; n < pageSize; n++)
    {
        data[n] = (char)i;
    }
    seg.unfixPage(frame, true);
};


/**
Tests min/max-Page requests
*/
void Hausaufgabe3_Test::test7()
{
    cout << "Start Test-7" << endl;
    createFile(DB_FILE.c_str(), DB_FILE_SIZE);
    SegmentInventory& segInv = DB::getDB().getSegmentInventory();
    SegmentManager& segMan = DB::getDB().getSegmentManager();
    Segment& seg = segMan.createSegment();
    Segment& seg2 = segMan.createSegment();
    //segInv.printInventory();

    for (unsigned i = 0; i < 16 * DB_FILE_SIZE - 1; i++)
    {
        cout << "Test7: " << i << endl;
        unsigned min = 1;
        unsigned max = min + (rand() % 5);
        unsigned one = 0, two = 0;

        if (rand() % 2 == 0)
        {
            unsigned old = seg.getPageCount();
            unsigned act = seg.requestPages(min, max).size();
            assert(act >= min && act <= max);
            assert(seg.getPageCount() == old + act);
            BufferFrame& frame = seg.getPage(one, false);
            assert(frame.getPageId() > 0);
            seg.unfixPage(frame, false);
            one++;
        }
        else
        {
            unsigned old = seg2.getPageCount();
            unsigned act = seg2.requestPages(min, max).size();
            assert(act >= min && act <= max);
            assert(seg2.getPageCount() == old + act);
            BufferFrame& frame = seg2.getPage(two, false);
            assert(frame.getPageId() > 0);
            seg2.unfixPage(frame, false);
            two++;
        }
        //segInv.printInventory();
    }

    segMan.removeSegment(seg);
    segMan.removeSegment(seg2);
    assert(segInv.getFreeCount() > 0);
    //segInv.printInventory();

    cout << "End Test-7" << endl;
}

void Hausaufgabe3_Test::createFile(const char* fileName, unsigned pageCount)
{
    //Create TestFile
    FILE* testFile = fopen(fileName, "w");
    char* data = new char[pageCount * pageSize];
    fwrite((void*)data, sizeof(char), pageCount * pageSize, testFile);
    fclose(testFile);
};

#endif
